/*
 * $Id: MailHelper.java,v 1.2 2009/08/20 20:07:37 navdeep_mahajan Exp $
 * 
 * Copyright (C) 2008 General Electric Company. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 * 
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
package com.ge.healthcare.autosc.common.util.mail;

import java.util.Iterator;
import java.util.Set;

import com.ge.healthcare.autosc.common.ASCLogger;
import com.ge.healthcare.autosc.common.ASCUtil;
import com.ge.healthcare.autosc.common.exceptions.PropertyException;
import com.ge.healthcare.autosc.common.util.ApplicationInfo;
import com.ge.healthcare.autosc.common.util.PropertyConstants;

/**
 * 
 * @author 212042946
 * MailValidatorHelper provides methods for validating the
 * format or size of each email element.
 */
public class MailHelper {

	private static final int BYTES_PER_KBYTE = 1024;
	
	/**
	 * Determine if mail body size is within limit. Evaluation is based
	 * on the MAIL_MAX_KBYTES in autosc.properties file. MAIL_MAX_KBYTES
	 * should not be less than 4KB.
	 * @param mailInfo
	 * @return true/false
	 * @throws MailValidatorException
	 */
	public static boolean isMailBodySizeValid(MailInfo mailInfo) 
		throws MailValidatorException {
		
		if(mailInfo.anyEmailBody()) {
			try {
				int maxSize = Integer.valueOf(ApplicationInfo.getInstance().getRequiredPropertyValue(
						PropertyConstants.MAIL_MAX_KBYTES));
				if(maxSize < 4) {
					ASCLogger.info(MailHelper.class, "isMailBodySizeValid",
						new StringBuffer()
						.append("The maximum mail size is configured as ")
						.append(maxSize)
						.append(", which is too small. It must be at least 4KB").toString());
					return false;
				}
	
				int maxBodySize = maxSize * BYTES_PER_KBYTE;
				if(mailInfo.anyEmailBody()) {
					if(mailInfo.getEmailBody().length() > maxBodySize) {
						ASCLogger.info(MailHelper.class, "isMailBodySizeValid", 
							new StringBuffer()
							.append("The mail body size is larger than the configured limit of ")
							.append(maxBodySize).toString());
						return false;
					}
				}
				return true;
			} catch (PropertyException pe) {
				StringBuffer msg = new StringBuffer()
					.append("Cannot validate mail body size. Exception\n")
					.append(pe.getMessage());
				ASCLogger.error(MailHelper.class, "isMailBodySizeValid", msg.toString());
				throw new MailValidatorException(msg.toString(), pe);
			}
		}
		return true;
	}
	
	/**
	 * Determine if total size of all attachments is within limit. Evaluation is based
	 * on the MAIL_MAX_KBYTES in autosc.properties file. MAIL_MAX_KBYTES
	 * should not be less than 4KB. 
	 * @param mailInfo
	 * @return true/false
	 * @throws MailValidatorException
	 */
	public static boolean isMailAttachmentSizeValid(MailInfo mailInfo) 
		throws MailValidatorException {
		if(mailInfo.anyAttachment()) {
			try {
				long maxSize = new Long(ApplicationInfo.getInstance().getRequiredPropertyValue(
						PropertyConstants.MAIL_MAX_KBYTES)).longValue();
				if(maxSize < 4) {
					ASCLogger.info(MailHelper.class, "isMailAttachmentSizeValid",
							new StringBuffer()
							.append("The maximum mail size is configured as ")
							.append(new Long(maxSize))
							.append(", which is too small. It must be at least 4KB").toString());
					return false;
				}
		
				// If we already have mail body, we need to consider the size of the
				// email body. Minimum body size is ~4KB
				long bodyTextSize = 4;
				if(mailInfo.anyEmailBody()) {
					bodyTextSize = mailInfo.getEmailBody().length();
				}
				long maxAttachmentSize = (maxSize - bodyTextSize) * BYTES_PER_KBYTE;
				if(mailInfo.anyAttachment()) {
					long totalSize = 0;
					Set<Attachment> attachments = mailInfo.getAttachements();
					Iterator<Attachment> attIter = attachments.iterator();
					while(attIter.hasNext()) {
						Attachment attachment = attIter.next();
						if(attachment.isValidAttachment()) {
							totalSize += attachment.getAttachmentDS().getFile().length();
						} else {
							StringBuffer msg = new StringBuffer();
							msg.append("Cannot calculate attachment size because of invalid attachment");
							msg.append("Attachment:\n");
							msg.append(attachment);
							throw new MailValidatorException(msg.toString());
						}
					}
					if(totalSize > maxAttachmentSize) {
						StringBuffer msg = new StringBuffer();
							msg.append("The mail attachment size is larger than the limit of ");
							msg.append(new Long(maxAttachmentSize));
						ASCLogger.error(MailHelper.class, "isMailAttachmentSizeValid", msg.toString());
						throw new MailValidatorException(msg.toString());
					}
				}
			} catch (PropertyException pe) {
				StringBuffer msg = new StringBuffer()
					.append("Cannot validate mail attachment size. Exception\n")
					.append(pe.getMessage());
				ASCLogger.error(MailHelper.class, "isMailAttachmentSizeValid", msg.toString());
				throw new MailValidatorException(msg.toString(), pe);
			}
		}
		return true;
	}
	
	/**
	 * Determine if the sender address is in valid format
	 * @param mailInfo
	 * @return true/false
	 * @throws MailValidatorException
	 */
	public static boolean isSenderAddressValid(MailInfo mailInfo) 
		throws MailValidatorException {
		if(mailInfo.anySenderAddress()) {
			try {
				String invalidChars =
					ApplicationInfo.getInstance().getRequiredPropertyValue(
							PropertyConstants.MAIL_INVALID_ADDRESS_CHARS);
				if(ASCUtil.containsInvalidChars(mailInfo.getSenderAddress(), invalidChars)) {
					return false;
				}
				
				return MailHelper.isAddressInRightFormat(mailInfo.getSenderAddress());
			} catch (PropertyException pe) {
				StringBuffer msg = new StringBuffer()
					.append("Cannot validate sender address. Exception\n")
					.append(pe.getMessage());
				ASCLogger.error(MailHelper.class, "isSenderAddressValid", msg.toString());
				throw new MailValidatorException(msg.toString(), pe);
			}
		}
		return true;
	}
	
	/**
	 * Determine if the recipient address is in valid format
	 * @param mailInfo
	 * @return true/false
	 * @throws MailValidatorException
	 */
	public static boolean isRecipientsAddressValid(MailInfo mailInfo) 
		throws MailValidatorException {
		if(mailInfo.anyRecipients()) {
			try {
				String invalidChars =
					ApplicationInfo.getInstance().getRequiredPropertyValue(
							PropertyConstants.MAIL_INVALID_ADDRESS_CHARS);
				return MailHelper.validateAddresses(mailInfo.getRecipients(), invalidChars);
			} catch (PropertyException pe) {
				StringBuffer msg = new StringBuffer()
					.append("Cannot validate receipient addresses. Exception\n")
					.append(pe.getMessage());
				ASCLogger.error(MailHelper.class, "isRecipientsAddressValid", msg.toString());
				throw new MailValidatorException(msg.toString(), pe);
			}
		}
		return true;
	}
	
	/**
	 * Determine if the CC recipient address is in valid format
	 * @param mailInfo
	 * @return true/false
	 * @throws MailValidatorException
	 */
	public static boolean isCcRecipientsAddressValid(MailInfo mailInfo) 
		throws MailValidatorException {
		if(mailInfo.anyCcRecipients()) {
			try {
				String invalidChars =
					ApplicationInfo.getInstance().getRequiredPropertyValue(
							PropertyConstants.MAIL_INVALID_ADDRESS_CHARS);
				return MailHelper.validateAddresses(mailInfo.getRecipients(), invalidChars);
			} catch (PropertyException pe) {
				StringBuffer msg = new StringBuffer()
					.append("Cannot validate CC receipient addresses. Exception\n")
					.append(pe.getMessage());
				ASCLogger.error(MailHelper.class, "isCcRecipientsAddressValid", msg.toString());
				throw new MailValidatorException(msg.toString(), pe);
			}	
		}
		return true;
	}
	
	/**
	 * Determine if the BCC recipient address is in valid format
	 * @param mailInfo
	 * @return true/false
	 * @throws MailValidatorException
	 */
	public static boolean isBccRecipientsAddressValid(MailInfo mailInfo)
		throws MailValidatorException {
		if(mailInfo.anyBccRecipients()) {
			try {
				String invalidChars =
					ApplicationInfo.getInstance().getRequiredPropertyValue(
							PropertyConstants.MAIL_INVALID_ADDRESS_CHARS);
				return MailHelper.validateAddresses(mailInfo.getRecipients(), invalidChars);	
			} catch (PropertyException pe) {
				StringBuffer msg = new StringBuffer()
				.append("Cannot validate BCC receipient addresses. Exception\n")
				.append(pe.getMessage());
				ASCLogger.error(MailHelper.class, "isBccRecipientsAddressValid", msg.toString());
				throw new MailValidatorException(msg.toString(), pe);
			}
		}
		return true;
	}
	
	private static boolean validateAddresses(String[] addresses, String invalidChars) {
		for(int idx = 0;idx < addresses.length;idx++) {
			String address = addresses[idx];
			if(ASCUtil.containsInvalidChars(address, invalidChars)) {
				return false;
			}
			if(MailHelper.isAddressInRightFormat(address) == false) {
				return false;
			}
		}
		return true;
	}
	
	private static boolean isAddressInRightFormat(String emailAddress) {
		if(emailAddress.length() < 1)
        {
            return false;
        }
		if(emailAddress.indexOf("@") < 0) {
			return false;
		}
        if(emailAddress.startsWith("."))
        {
            return false;
        }
        if(emailAddress.indexOf("..") > -1)
        {
            return false;
        }
        if (emailAddress.indexOf(".@") > -1)
        {
            return false;
        }
        if (emailAddress.indexOf("@.") > -1)
        {
            return false;
        }
        return true;
	}
}
